﻿namespace Blaze.Framework.Loaders
{
    using System;
    using System.Collections;
    using System.IO;
    using Logging;
    using UnityEngine;

    /// <summary>
    /// 用于为流中的数据加载资源包。
    /// </summary>
    public class StreamAssetBundleLoader : AssetLoaderBase
    {
        /// <summary>
        /// 获取加载完成的资源包。
        /// </summary>
        public AssetBundle AssetBundle => (AssetBundle) Result;

        public override float Progress
        {
            get
            {
                if (mCreateRequest == null)
                    return 0;
                return mCreateRequest.progress;
            }
        }

        /// <summary>
        /// 设置一个值表示当资源包加载结束后，是否由<see cref="StreamAssetBundleLoader"/>来关闭流。
        /// </summary>
        /// <param name="isAuto">是否自动关闭流</param>
        public StreamAssetBundleLoader SetAutoClose(bool isAuto)
        {
            mIsAuto = isAuto;
            return this;
        }

        /// <summary>
        /// 设置资源包所对应的内存数据。
        /// </summary>
        public StreamAssetBundleLoader SetStream(Stream stream)
        {
            mStream = stream;
            return this;
        }

        /// <summary>
        /// 当加载器需要被释放时调用此方法。
        /// </summary>
        protected override void OnDispose()
        {
            if (mRunner != null)
            {
                CoroutineManager.Stop(mRunner);
                mRunner = null;
            }

            if (AssetBundle != null)
                AssetBundle.Unload(false);
            mStream = null;
            base.OnDispose();
        }

        /// <summary>
        /// 当加载器启动时调用此方法。
        /// </summary>
        protected override void OnStart()
        {
            switch (Mode)
            {
                case LoadAssetMode.Async:
                    mCreateRequest = AssetBundle.LoadFromStreamAsync(mStream);
                    if (mCreateRequest == null)
                    {
                        mLog.Error("[StreamAssetBundleLoader]create from stream failed " + Uri);
                        Finish("create from stream failed", null);
                        autoClose();
                        return;
                    }
                    mRunner = CoroutineManager.StartNew(wait(mCreateRequest));
                    break;
                case LoadAssetMode.Sync:
                    Finish(null, AssetBundle.LoadFromStream(mStream));
                    autoClose();
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void autoClose()
        {
            if (mIsAuto)
                mStream?.Close();
        }

        private IEnumerator wait(AssetBundleCreateRequest req)
        {
            while (!req.isDone)
                yield return null;
            Finish(null, req.assetBundle);
            autoClose();
        }

        private static readonly Log mLog = LogManager.GetLogger(typeof(StreamAssetBundleLoader));
        private Stream mStream;
        private bool mIsAuto = true;
        private AssetBundleCreateRequest mCreateRequest;
        private Coroutine mRunner;
    }
}